﻿/* 
   Copyright 2011 LUCA Studios LLC & John Evans

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

     http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
*/

namespace LUCAStudios.Silverlight.Controls
{
    using System;
    using System.Windows;
    using System.Windows.Controls;
    using System.Linq;

    /// <summary>
    /// Contains extension methods supporting various operations of the LUCA Studios UI Framework
    /// Many of the extension methods return the calling object, to support declarative chaining
    /// </summary>
    public static class FrameworkHelpers
    {
        //TODO: Implement remove/restore LUCATabItem functions

        #region overloaded .Remove()

        /// <summary>
        /// Removes a ControlBase control from it's ContainerContext
        /// </summary>
        /// <param name="control"></param>
        /// <returns></returns>
        public static ControlBase Remove(this ControlBase control)
        {
            if (control.ContainerContext != null)
                control.ContainerContext.RemoveChild(control);

            return control;
        }

        /// <summary>
        /// Removes a LUCAWindowBase control from it's ContainerContext
        /// </summary>
        /// <param name="control"></param>
        /// <returns></returns>
        public static WindowBase Remove(this WindowBase control)
        {

            //in this case, just pass up to the base class function
            ((ControlBase)control).Remove();
            return control;
        }

        /// <summary>
        /// Removes a menu from the visual tree
        /// </summary>
        /// <param name="control"></param>
        /// <returns></returns>
        public static Menu Remove(this Menu control)
        {
            control.CloseMenu();
            //return the same control for extension method chaining
            return control;
        }

        /// <summary>
        /// Removes a MenuStrip from the visual tree
        /// </summary>
        /// <param name="control"></param>
        /// <returns></returns>
        public static LUCAMenuStrip Remove(this LUCAMenuStrip control)
        {
            throw new NotImplementedException();
            //return control;
        }

        /// <summary>
        /// Removes a LUCATabItem from it's ContainerContext (should be LUCATabWindow)
        /// </summary>
        /// <param name="control"></param>
        /// <returns></returns>
        public static LUCATabItem Remove(this LUCATabItem control)
        {
            if (!control.IsChildOfContainerContext())
                throw new LUCAControlException("Expected control to be a child its' ContainerContext, or ContainerContext may be null.");

            control.ContainerContext.RemoveChild(control);

            control.StoreWindowProperties(control.PreviousContainerContext.ContainerContext as WindowBase);

            //remove the container if this control is the last remaining control
            if (control.PreviousContainerContext.ContainerContext.Children.Count == 0)
            {
                control.PreviousContainerContext.ContainerContext.Remove();

                if (control.PreviousContainerContext.ContainerContext.PreviousContainerContext.ContainerContext is LUCAStackPanel)
                    control.PreviousContainerContext = control.PreviousContainerContext.ContainerContext.PreviousContainerContext;
                else
                {
                    control.PreviousContainerContext = null;
                }
            }

            return control;
        }


        /// <summary>
        /// Removes a LUCATabWindow from it's ContainerContext.  Also removes all
        /// LUCATabItems from it's collection first
        /// </summary>
        /// <param name="control"></param>
        /// <param name="preserveChildren">when True, does not remove LUCATabItem controls from it's collection</param>
        /// <returns></returns>
        public static LUCATabWindow Remove(this LUCATabWindow control, bool preserveChildren)
        {
            //remove the kids one by one
            if (!preserveChildren)
                control
                    .Children
                    .OfType<LUCATabItem>()
                    .ToObservable()
                    .ObserveOnDispatcher()
                    .Subscribe(o => o.Remove());

            (control).Remove();

            return control;
        }

        #endregion

        #region overloaded .Restore()

        /// <summary>
        /// Attempts to restore a LUCAWindowBase control to it's previous container if it is still viable.
        /// If not, then restores the control to the LUCALayoutPanel as a floating window.
        /// </summary>
        /// <param name="control"></param>
        /// <returns></returns>
        public static WindowBase Restore(this WindowBase control)
        {
            //TODO: restore position if in LayoutPanel

            if (control.PreviousContainerContext == null)
            {
                //restore the window into the layoutcontainer as a floating window
                var llp = control.LayoutContext;
                if (llp == null) throw new LUCAControlException("Expected LayoutContext to be non-null.");

                control.VisualState = WindowLayoutState.Floating;
                llp.AddChild(control);
            }
            else
            {
                //restore the window to it's last known position
                if (control.PreviousContainerContext.Index > control.PreviousContainerContext.ContainerContext.Children.Count
                    || control.PreviousContainerContext.WasLastItemInContainer)
                {
                    control.PreviousContainerContext.ContainerContext.AddChild(control);
                }
                else
                {
                    control.PreviousContainerContext.ContainerContext.InsertChild(control.PreviousContainerContext.Index, control);
                }
            }

            return control;
        }

        /// <summary>
        /// Attempts to restore a LUCATabItem into it's previous LUCATabWindow if that window is still viable.
        /// Otherwise a new LUCATabWindow is created and place into the LayoutContext with the previous LUCATabWindow
        /// Settings preserved. 
        /// </summary>
        /// <param name="control"></param>
        /// <returns></returns>
        public static LUCATabItem Restore(this LUCATabItem control)
        {
            if (control.PreviousContainerContext == null || control.PreviousContainerContext.ContainerContext.ContainerContext == null)
            {
                //restore the window into the layoutcontainer as a floating window
                var newWin = new LUCATabWindow
                                 {
                                     WindowID = Guid.NewGuid().ToString()
                                 };

                newWin
                    .AssignStoredWindowProperties(control)
                    .AddChild(control);

                newWin.LayoutContext = control.LayoutContext;

                newWin.Restore();
            }
            else
            {
                if (control.PreviousContainerContext.ContainerContext is LUCAStackPanel)
                {
                    var pc = control.PreviousContainerContext;
                    var newWin = new LUCATabWindow
                    {
                        WindowID = Guid.NewGuid().ToString()
                    };

                    newWin
                        .AssignStoredWindowProperties(control)
                        .AddChild(control);

                    newWin.LayoutContext = control.LayoutContext;

                    newWin.PreviousContainerContext = pc;

                    newWin.Restore();
                }
                else if (control.PreviousContainerContext.ContainerContext is LUCATabWindow)
                {
                    //restore the window to it's last known position
                    if (control.PreviousContainerContext.Index >
                        control.PreviousContainerContext.ContainerContext.Children.Count
                        || control.PreviousContainerContext.WasLastItemInContainer)
                    {
                        control.PreviousContainerContext.ContainerContext.AddChild(control);
                    }
                    else
                    {
                        control.PreviousContainerContext.ContainerContext.InsertChild(
                            control.PreviousContainerContext.Index, control);
                    }
                    return control;
                }
                else
                {
                    throw new LUCAControlException("Previous ContainerContext for LUCATabItem is not of expected Type");
                }
            }

            return control;
        }

        #endregion

        /// <summary>
        /// Returns true if the given control has a ContainerContext set and the control is a child of that ContainerContext
        /// </summary>
        /// <param name="control"></param>
        /// <returns></returns>
        public static bool IsChildOfContainerContext(this ControlBase control)
        {
            return control.ContainerContextSet() && control.ContainerContext.Children.Contains(control);
        }

        #region private functions

        // ReSharper disable UnusedMethodReturnValue.Local
        private static ControlBase StoreWindowProperties(this ControlBase control, WindowBase targetWindow)
        // ReSharper restore UnusedMethodReturnValue.Local
        {
            //preserve LUCAStackPanel attached properties for later restores into new LUCATabWindow
            if (targetWindow.ContainerContextSet())
            {
                control.StoreProperty(LUCAStackPanel.ColumnDefinitionProperty,
                                      LUCAStackPanel.GetColumnDefinition(targetWindow), true);
                control.StoreProperty(LUCAStackPanel.RowDefinitionProperty,
                                      LUCAStackPanel.GetRowDefinition(targetWindow), true);

                var pos = targetWindow.GetPosition(control.LayoutContext.RootVisual);

                control.StoreProperty(Canvas.LeftProperty, pos.X, true);
                control.StoreProperty(Canvas.TopProperty, pos.Y, true);
                control.StoreProperty(FrameworkElement.WidthProperty, targetWindow.Width, true);
                control.StoreProperty(FrameworkElement.HeightProperty, targetWindow.Height, true);
            }

            return control;
        }

        private static WindowBase AssignStoredWindowProperties(this WindowBase control, ControlBase controlToRestoreFrom)
        {
            var cd = controlToRestoreFrom.RetrieveProperty<ColumnDefinition>(LUCAStackPanel.ColumnDefinitionProperty);
            var rd = controlToRestoreFrom.RetrieveProperty<RowDefinition>(LUCAStackPanel.RowDefinitionProperty);

            if (cd != default(ColumnDefinition))
            {
                var ncd = new ColumnDefinition { Width = cd.Width };
                LUCAStackPanel.SetColumnDefinition(control, ncd);
            }

            if (rd != default(RowDefinition))
            {
                var nrw = new RowDefinition { Height = rd.Height };
                LUCAStackPanel.SetRowDefinition(control, nrw);
            }

            control.XOffset = controlToRestoreFrom.RetrieveProperty<double>(Canvas.LeftProperty);
            control.YOffset = controlToRestoreFrom.RetrieveProperty<double>(Canvas.TopProperty);
            control.Width = controlToRestoreFrom.RetrieveProperty<double>(FrameworkElement.WidthProperty);
            control.Height = controlToRestoreFrom.RetrieveProperty<double>(FrameworkElement.HeightProperty);

            return control;
        }

        private static bool ContainerContextSet(this ControlBase control)
        {
            return control.ContainerContext != null;
        }

        #endregion
    }
}
